package utils

import (
	"context"
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/aidarkhanov/nanoid/v2"
	"github.com/bwmarrin/snowflake"
	"github.com/gin-gonic/gin"
	"github.com/xuri/excelize/v2"
	"io/ioutil"
	"iot-base/common/notify"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"
	"time"
)

type H map[string]interface{}

// GetRandomString 生产随机字符串.
func GetRandomString(l int) string {
	str := "0123456789abcdefghijklmnopqrstuvwxyz"
	strBytes := []byte(str)
	result := []byte{}
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	for i := 0; i < l; i++ {
		result = append(result, strBytes[r.Intn(len(strBytes))])
	}
	return string(result)
}

// GetMd5Pwd 获取md5密码.
func GetMd5Pwd(pwd, salf string) string {
	return GetMd5(pwd + salf)
}

func GetMd5(data string) string {
	m := md5.New()
	m.Write([]byte(data))
	ret := hex.EncodeToString(m.Sum(nil))
	return ret
}
func GetUid() int64 {
	id, _ := GetSnowflakeId(1)
	return id
}

var node *snowflake.Node

// SetSnowflakeParam 设置nodeid等参数.
func SetSnowflakeParam(nid int64, epoch int64, nodebits uint8, stepbits uint8) error {
	if nid > 512 {
		return errors.New("node is too large")
	}
	snowflake.Epoch = epoch       //1620574027000
	snowflake.NodeBits = nodebits //5
	snowflake.StepBits = stepbits //7
	var err error
	node, err = snowflake.NewNode(nid)
	return err
}

// GetSnowflakeId 获取id 1620574027000 为2021.5.9时间戳
func GetSnowflakeId(n int64) (int64, error) {
	if node == nil {
		err := SetSnowflakeParam(n, 1620574027000, 5, 7)
		if err != nil {
			return 0, err
		}
	}
	if node != nil {
		return node.Generate().Int64(), nil
	}
	return 0, errors.New("node is nil")

}

// Mkdir 定义一个创建文件目录的方法
func Mkdir(basePath string) string {
	//	1.获取当前时间,并且格式化时间
	folderName := time.Now().Format("2006/01/02")
	folderPath := filepath.Join(basePath, folderName)
	//使用mkdirall会创建多层级目录
	_ = os.MkdirAll(folderPath, os.ModePerm)
	return folderPath
}

func StructToMap(v interface{}) (map[string]interface{}, error) {
	m := make(map[string]interface{})
	data, err := json.Marshal(&v)
	if err != nil {
		return m, err
	}

	err = json.Unmarshal(data, &m)
	return m, err
}

func StructTo2Map(obj interface{}) (map[string]interface{}, error) {
	defer func() {
		if f := recover(); f != nil {
		}
	}()
	t := reflect.TypeOf(obj)
	if t.Kind() != reflect.Struct {
		return nil, errors.New("data is not struct")
	}
	v := reflect.ValueOf(obj)
	var data = make(map[string]interface{})
	for i := 0; i < t.NumField(); i++ {
		if v.Field(i).Kind() == reflect.Struct {
			data[t.Field(i).Name], _ = StructTo2Map(v.Field(i).Interface())
		} else {
			data[t.Field(i).Name] = v.Field(i).Interface()
		}
	}
	return data, nil
}

// StructToTagMap Struct 转 map  根据 json tag 作为map的key
func StructToTagMap(obj interface{}) (map[string]interface{}, error) {
	defer func() {
		if f := recover(); f != nil {
		}
	}()
	t := reflect.TypeOf(obj)
	if t.Kind() != reflect.Struct {
		return nil, errors.New("data is not struct")
	}
	v := reflect.ValueOf(obj)
	var data = make(map[string]interface{})
	for i := 0; i < t.NumField(); i++ {
		if v.Field(i).Kind() == reflect.Struct {
			tags := strings.Split(t.Field(i).Tag.Get("json"), ",")
			data[tags[0]], _ = StructTo2Map(v.Field(i).Interface())
		} else {
			tags := strings.Split(t.Field(i).Tag.Get("json"), ",")
			data[tags[0]] = v.Field(i).Interface()
		}
	}
	return data, nil
}

//StructToJson 结构体转json.
func StructToJson(v interface{}) (string, error) {
	resp, err := json.Marshal(v)
	if err != nil {
		return "", err
	}
	return string(resp), nil
}

//JsonToStruct json转结构体.
func JsonToStruct(v interface{}, jsonStr string) error {
	err := json.Unmarshal([]byte(jsonStr), v)
	if err != nil {
		return err
	}
	return nil
}

func HttpGet(url string) ([]byte, error) {
	resp := make([]byte, 0)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
	defer cancel()
	done := make(chan error, 1)
	AntsPool.Submit(func() {
		httpClient := &http.Client{
			Timeout: time.Second * 2,
		}
		httpResp, err := httpClient.Get(url)
		defer httpResp.Body.Close()
		if err != nil {
			done <- err
		}
		resp, err = ioutil.ReadAll(httpResp.Body)
		done <- err
	})
	select {
	case err := <-done:
		return resp, err
	case <-ctx.Done():
		return resp, nil
	}
}

func StringToTime(data string) (time.Time, error) {
	return time.ParseInLocation("2006-01-02 15:04:05", data, time.Local)
}

func GetNowToString() string {
	return time.Now().Format("2006-01-02 15:04:05")
}

func TimeToString(layout string, datetime time.Time) string {
	return datetime.Format(layout)
}
func Save(option interface{}, filename string) {
	//os.Mkdir(config.Conf.Qt.ConfigPath, os.ModePerm)
	//fmt.Println("测试",filename)
	filePtr, err := os.Create(filename)
	if err != nil {
		return
	}
	defer filePtr.Close()
	d, _ := json.Marshal(option)
	filePtr.WriteString(string(d))
}

func InterfaceToString(param interface{}) string {
	switch param.(type) {
	case int:
		return strconv.Itoa(param.(int))
	case int64:
		return strconv.FormatInt(param.(int64), 10)
	case float64:
		return strconv.FormatFloat(param.(float64), 'f', 2, 64)
	default:
		return ""

	}
}

//AutoID 自动生成id
func AutoID() string {
	v, _ := nanoid.New()
	return fmt.Sprintf("%v", v)
}

func SendEdgeCmd(action string, edgeId string, data any) ([]byte, error) {
	b, _ := json.Marshal(data)
	// 发送命令
	result, err := notify.CloudSendCmd(true, edgeId, action, b)
	if err != nil {
		return nil, err
	}
	if result.Code == 0 {
		return result.Result, nil
	} else {
		return nil, errors.New(result.Msg)
	}
}

func SendCloudCmd(action string, data any) ([]byte, error) {
	// 发送命令
	b, _ := json.Marshal(data)
	result, err := notify.EdgeReportData(true, action, b)
	if err != nil {
		return nil, err
	}
	if result.Code == 0 {
		return result.Result, nil
	} else {
		return nil, errors.New(result.Msg)
	}
}

// ExportExcelByStruct excel导出(数据源为Struct)
func ExportExcelByStruct(c *gin.Context, titleList []string, data []interface{}, fileName string, sheetName string) error {
	f := excelize.NewFile()
	f.SetSheetName("Sheet1", sheetName)
	header := make([]interface{}, 0)
	for _, v := range titleList {
		header = append(header, v)
	}

	streamWriter, err := f.NewStreamWriter(sheetName)
	if err != nil {
		return err
	}
	streamWriter.SetColWidth(1, 50, 17)
	_ = streamWriter.SetRow("A1", header)
	rowNum := 1
	for _, v := range data {
		t := reflect.TypeOf(v)
		value := reflect.ValueOf(v)
		row := make([]interface{}, 0)
		for l := 0; l < t.NumField(); l++ {
			val := value.Field(l).Interface()
			row = append(row, val)
		}
		rowNum++
		_ = streamWriter.SetRow("A"+strconv.Itoa(rowNum), row)
	}
	if err = streamWriter.Flush(); err != nil {
		return err
	}
	disposition := fmt.Sprintf("attachment; filename=%s-%s.xlsx", url.QueryEscape(fileName), time.Now().Format("2006_02_01"))
	c.Writer.Header().Set("Content-Type", "application/octet-stream")
	c.Writer.Header().Set("Content-Disposition", disposition)
	c.Writer.Header().Set("Content-Transfer-Encoding", "binary")
	c.Writer.Header().Set("Access-Control-Expose-Headers", "Content-Disposition")
	return f.Write(c.Writer)
}

// Letter 遍历a-z
func Letter(length int) []string {
	var str []string
	for i := 0; i < length; i++ {
		str = append(str, string(rune('A'+i)))
	}
	return str
}

func ExportExcelBySlice(c *gin.Context, titleList []interface{}, data []interface{}, fileName string, sheetName string) error {
	f := excelize.NewFile()
	f.SetSheetName("Sheet1", sheetName)
	streamWriter, err := f.NewStreamWriter(sheetName)
	if err != nil {
		return err
	}
	streamWriter.SetColWidth(1, 50, 17)
	// 添加head
	_ = streamWriter.SetRow("A1", titleList)
	for k, v := range data {
		temp, ok := v.([]interface{})
		if !ok {
			return errors.New("not is []interface{}")
		}
		rowNum := strconv.Itoa(k + 2)
		_ = streamWriter.SetRow("A"+rowNum, temp)

	}
	if err = streamWriter.Flush(); err != nil {
		return err
	}
	disposition := fmt.Sprintf("attachment; filename=%s-%s.xlsx", url.QueryEscape(fileName), time.Now().Format("2006_02_01"))
	c.Writer.Header().Set("Content-Type", "application/octet-stream")
	c.Writer.Header().Set("Content-Disposition", disposition)
	c.Writer.Header().Set("Content-Transfer-Encoding", "binary")
	c.Writer.Header().Set("Access-Control-Expose-Headers", "Content-Disposition")
	return f.Write(c.Writer)
}
